package xtrace

import (
	"context"
	"fmt"
	"os"
	"time"

	"gitee.com/go-mid/infra/xlog"
	"github.com/uber/jaeger-client-go"
	"github.com/uber/jaeger-client-go/config"
)

const (
	defaultAgentHost = "127.0.0.1"
	defaultAgentPort = "6831"
)

// Environment variables for jaeger agent
const (
	EnvJaegerAgentHost = "JAEGER_AGENT_HOST"
	EnvJaegerAgentPort = "JAEGER_AGENT_PORT"
)

const (
	apolloKeyJaegerSamplerTypePrefix  = "jaeger_sampler_type."
	apolloKeyJaegerSamplerParamPrefix = "jaeger_sampler_param."
	apolloJaegerSamplerGlobalName     = "default_global"

	defaultValueJaegerSamplerType          = jaeger.SamplerTypeRateLimiting
	defaultValueJaegerSamplerParam float64 = 1

	defaultGetApolloTimeout = 3 * time.Second
)

// TracerConfig keeps metadata for tracer
type TracerConfig struct {
	Payload interface{}
}

// TracerConfigManager defines the interface for an concrete implementation of config manager.
type TracerConfigManager interface {
	GetConfig(serviceName string, tracerType TracerType) TracerConfig
}

func newTracerConfigManager() TracerConfigManager {
	return newSimpleTracerConfigManager()
}

type simpleManager struct{}

type apolloManager struct {
}

func newSimpleTracerConfigManager() *simpleManager {
	return &simpleManager{}
}

func newApolloTracerConfigManager() *apolloManager {
	return &apolloManager{}
}

func (s *simpleManager) GetConfig(serviceName string, tracerType TracerType) TracerConfig {
	if tracerType != TracerTypeJaeger {
		xlog.Panicf(context.Background(), "unknown tracer type %s for simpleManager", tracerType)
	}

	agentHost, agentPort := defaultAgentHost, defaultAgentPort

	if h, ok := os.LookupEnv(EnvJaegerAgentHost); ok {
		agentHost = h
	}

	if p, ok := os.LookupEnv(EnvJaegerAgentPort); ok {
		agentPort = p
	}

	return TracerConfig{
		Payload: config.Configuration{
			ServiceName: serviceName,
			Disabled:    false,
			RPCMetrics:  false,
			Sampler:     defaultSamplerConfig(),
			Reporter:    defaultReporterConfig(agentHost, agentPort),
			Headers:     defaultHeadersConfig(),
		},
	}
}

func getApolloKeysJaegerSamplerWithName(name string) (string, string) {
	keySamplerType := apolloKeyJaegerSamplerTypePrefix + name
	keySamplerParam := apolloKeyJaegerSamplerParamPrefix + name
	return keySamplerType, keySamplerParam
}

func isMyJaegerSamplerConfigKey(key string, serviceName string) bool {
	serviceSamplerType, serviceSamplerParam := getApolloKeysJaegerSamplerWithName(serviceName)
	globalSamplerType, globalSamplerParam := getApolloKeysJaegerSamplerWithName(apolloJaegerSamplerGlobalName)
	return key == serviceSamplerType || key == serviceSamplerParam || key == globalSamplerType || key == globalSamplerParam
}

func defaultSamplerConfig() *config.SamplerConfig {
	return &config.SamplerConfig{
		Type:  defaultValueJaegerSamplerType,
		Param: defaultValueJaegerSamplerParam,
	}
}

func defaultReporterConfig(agentHost, agentPort string) *config.ReporterConfig {
	return &config.ReporterConfig{
		LocalAgentHostPort: getAgentAddr(agentHost, agentPort),
	}
}

func getAgentAddr(agentHost, agentPort string) string {
	return fmt.Sprintf("%s:%s", agentHost, agentPort)
}

func defaultHeadersConfig() *jaeger.HeadersConfig {
	return &jaeger.HeadersConfig{
		JaegerDebugHeader:        TraceDebugHeader,
		JaegerBaggageHeader:      TraceBaggageHeader,
		TraceContextHeaderName:   TraceContextHeaderName,
		TraceBaggageHeaderPrefix: TraceBaggageHeaderPrefix,
	}
}
